
#include <BThread>
#include "member_BThread.h"

using namespace BWE;

#define member					(*(member_BThread*)_ptr)
#define member_allocate()		_ptr = new member_BThread(this)
#define member_release()		delete (member_BThread*)_ptr

#ifdef linux
#include <errno.h>

void* thead_routine(void* parameter)
{
	BThread* thread = (BThread*)parameter;
	pthread_t handle = thread_member(thread)->handle;
	while (thread && !thread_member(thread)->done)
	{
		BReal tick = bTime();
		thread_member(thread)->run();
		thread_member(thread)->frameCount++;
		if (tick - thread_member(thread)->frameTick > 1.0)
		{
			thread_member(thread)->frameTick = tick;
			thread_member(thread)->realFrequency = thread_member(thread)->frameCount;
			thread_member(thread)->frameCount = 0;
		}
		if (thread_member(thread)->frequency != 0.0)
		{
			BReal time = bTime() - tick;
			BReal sleepTime = 1.0 / thread_member(thread)->frequency - time;
			thread->sleep(sleepTime);
		}
	}
	printf("thread %lu over!\n", handle);
	return &(thread_member(thread)->code);
}

BThread::BThread()
{
	member_allocate();
	if (0 != pthread_cond_init(&member.cond, 0))
	{
		puts("thread error : failed to init thread cond.");
		return;
	}
	if(0 != pthread_mutex_init(&member.mutex, 0))
	{
		puts("thread error : failed to init thread cond.");
		return;
	}
}
BThread::~BThread()
{
	pthread_cond_signal(&member.cond);
	pthread_cond_destroy(&member.cond);
	pthread_mutex_destroy(&member.mutex);
	if(member.handle)
	{
		pthread_detach(member.handle);
	}
	member_release();
}

bool BThread::start()
{
	pthread_t handle = member.handle;
	if (member.handle)
	{
		if(member.sleeping)
		{
			pthread_cond_signal(&member.cond);
			member.sleeping = false;
		}
	}
	else
	{
		int ret = pthread_create(&member.handle, 0, thead_routine, this);
		if(ret)
		{
			perror("thread error : failed to create thread.");
			return false;
		}
	}
	return true;
}
bool BThread::pause()
{
	return this->sleep(-1.0);
}

bool BThread::sleep(double seconds)
{
	if (member.handle == 0)
		return false;
	if(seconds == 0.0)
		return start();
	if(seconds < 0.0)
	{
		member.sleeping = true;
		pthread_cond_wait(&member.cond, &member.mutex);
		member.sleeping = false;
	}
	else
	{
		member.sleeping = true;
		timeval now;
		gettimeofday(&now, NULL);
		timespec abstime;
		abstime.tv_sec = now.tv_sec + (long)seconds;
		abstime.tv_nsec = now.tv_usec * 1000 + long((seconds - (long)seconds) * 1000000000.0);
		if(abstime.tv_nsec > 1000000000)
		{
			abstime.tv_sec++;
			abstime.tv_nsec -= 1000000000;
		}
		int ret = pthread_cond_timedwait(&member.cond, &member.mutex, &abstime);
		if(ret == EINVAL)
			perror("The value specified by abstime is invalid.");
		if(ret == EPERM)
			perror("The mutex was not owned by the current thread at the time of the call.");     
		//::usleep(seconds * 1000000.0);
		member.sleeping = false;
	}
	return true;
}

bool BThread::wait()
{
	int ret = pthread_cond_wait(&member.cond, &member.mutex);
	if(ret)
	{
		puts("error : thread condition wait failed.");
		return false;
	}
	return true;
}

bool BThread::done(int code)
{
	if (member.handle && !member.done)
	{
		member.done = true;
		member.code = code;
		if (member.sleeping)
		{
			pthread_cond_signal(&member.cond);
		}
		emit(Signal_Done, code);
		return true;
	}
	return false;
}

#endif

#ifdef _WIN32

DWORD WINAPI thread_routine(void* param)
{
	BThread* thread = (BThread*)param;
	while (thread && !thread_member(thread)->done)
	{
		double tick = bTime();
		thread_member(thread)->run();
		thread_member(thread)->frameCount++;
		if (tick - thread_member(thread)->frameTick > 1.0)
		{
			thread_member(thread)->frameTick = tick;
			thread_member(thread)->realFrequency = thread_member(thread)->frameCount;
			thread_member(thread)->frameCount = 0;
		}
		if (thread_member(thread)->frequency < 0)
		{
			thread->pause();
		}
		if (thread_member(thread)->frequency > 0)
		{
			double time = bTime() - tick;
			double sleepTime = 1.0 / thread_member(thread)->frequency - time;
			if (sleepTime > 0)
			{
				thread->sleep(sleepTime);
			}
		}
	}
	return thread_member(thread)->code;
}

BThread::BThread()
{
	member_allocate();
	member.handle = ::CreateThread(0, 0, thread_routine, this, CREATE_SUSPENDED, &member.ID);
}
BThread::~BThread()
{
	if (member.handle)
	{
		TerminateThread(member.handle, 0);
		::CloseHandle(member.handle);
	}
	if (member.htimer)
	{
		::CloseHandle(member.htimer);
	}
	member_release();
}

bool BThread::start()
{
	HANDLE handle = member.handle;
	if (member.handle && member.sleeping)
	{
		DWORD ret = ResumeThread(member.handle);
		member.sleeping = false;
		return true;
	}
	return false;
}
bool BThread::pause()
{
	if (member.handle && !member.sleeping)
	{
		member.sleeping = true;
		DWORD ret = SuspendThread(member.handle);
		member.sleeping = false;
		return true;
	}
	return true;
}
bool BThread::sleep(double seconds)
{
	if(!member.sleeping)
	{
		if(seconds == 0.0)
		{
			return start();
		}
		if(seconds < 0.0)
		{
			member.sleeping = !SuspendThread(member.handle);
			return member.sleeping;
		}
		else
		{
			if(member.htimer == 0)
				member.htimer = ::CreateWaitableTimer(0, true, 0);

			if(member.htimer == 0)
				return false;

			LARGE_INTEGER time;
			time.QuadPart = LONGLONG(seconds * -10000000);

			if(::SetWaitableTimer(member.htimer, &time, 0, 0, 0, 0) == false)
				return false;

			if(::WaitForSingleObject(member.htimer, INFINITE) == WAIT_FAILED)
				return false;
		}
	}
	return true;
}
bool BThread::wait()
{
	DWORD ret = WaitForSingleObject(member.handle, INFINITE);
	return ret != WAIT_FAILED;
}

bool BThread::done(int code)
{
	AutoLock lock(this);
	if (member.handle && !member.done)
	{
		member.done = true;
		member.code = code;
		if (member.sleeping)
		{
			DWORD ret = SuspendThread(member.handle);
		}
		emit(Signal_Done, code);
		return true;
	}
	return false;
}

#endif

void BThread::setFrequency(int frequency)
{
	AutoLock lock(this);
	if (member.frequency != frequency)
	{
		member.frequency = frequency;
	}
}
int BThread::frequency()
{
	AutoLock lock(this);
	return member.frequency;
}
int BThread::realFrequency()
{
	AutoLock lock(this);
	return member.realFrequency;
}

bool BThread::sleeping() const
{
	return member.sleeping;
}


